%%%-------------------------------------------------------------------
%%% @author Administrator
%%% @copyright (C) 2023, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 12. 4月 2023 14:52
%%%-------------------------------------------------------------------
-module(sc_app).
-behaviour(application). % 行为模式声明
-export([start/2, stop/1]). % 导出的行为模式回调函数
-define(WAIT_FOR_RESOURCES, 2500).
start(_StartType, _StartArgs) ->
  ok = ensure_contact(), % 在start()中加入这一行
  resource_discovery:add_local_resource(simple_cache, node()),
  resource_discovery:add_target_resource_type(simple_cache),
  resource_discovery:trade_resources(),
  timer:sleep(?WAIT_FOR_RESOURCES),

  sc_store:init(), % 存储初始化

  case sc_sup:start_link() of % 启动根监督者
    {ok, Pid} ->
      {ok, Pid};
    Other ->
      {error, Other}
  end.

ensure_contact() ->
  DefaultNodes = ['contact1@Erlang', 'contact2@Erlang'],
  case get_env(simple_cache, contact_nodes, DefaultNodes) of % ❶ 查询联络节点的配置信息
    [] ->
      {error, no_contact_nodes};
    ContactNodes ->
      ensure_contact(ContactNodes)
  end.
ensure_contact(ContactNodes) ->
  Answering = [N || N <- ContactNodes, net_adm:ping(N) =:= pong], % ❷ ping 列表中的节点
  case Answering of
    [] ->
      {error, no_contact_nodes_reachable};
    _ ->
      DefaultTime = 6000,
      WaitTime = get_env(simple_cache, wait_time, DefaultTime), % ❸ 查询等待超时的配置信息
      wait_for_nodes(length(Answering), WaitTime)
  end.
wait_for_nodes(MinNodes, WaitTime) ->
  Slices = 10,
  SliceTime = round(WaitTime/Slices),
  wait_for_nodes(MinNodes, SliceTime, Slices). % ❹ 进入等待循环
wait_for_nodes(_MinNodes, _SliceTime, 0) ->
  ok;
wait_for_nodes(MinNodes, SliceTime, Iterations) ->
  case length(nodes()) > MinNodes of % ❺ 检查是否已经连上了足够多的节点
    true ->
      ok;
    false ->
      timer:sleep(SliceTime),
      wait_for_nodes(MinNodes, SliceTime, Iterations - 1)
  end.
get_env(AppName, Key, Default) ->
  case application:get_env(AppName, Key) of % ❻ 查询配置信息
    undefined -> Default;
    {ok, Value} -> Value
  end.

stop(_State) ->
  ok.
